/*
 * GClubAbstractIntCupHistoryRecord.java
 * Created on 23 nov. 2008
 *
 * Glacéo Internet Platform
 * http://sourceforge.net/projects/chm/
 *
 * Copyright (c) 2005-2009, Jens and Jan Janke (VirtualHockey Project)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.hockeo.vhbase.data.dao.club;

import java.math.BigDecimal;

import javax.persistence.Column;
import javax.persistence.Entity;

import com.hockeo.vhbase.data.dao.contest.GContestRound;
import com.hockeo.vhbase.data.dao.contest.GKO3ContestElement;
import com.hockeo.vhbase.data.dao.contest.GRoundRobinContestElement;
import com.hockeo.vhbase.data.dao.match.GKOMatch;
import com.hockeo.vhbase.data.dao.season.GSeason;
import com.hockeo.vhbase.data.dao.standings.GStandings;
import com.hockeo.vhbase.data.helper.GContestRoundFormat;
import com.hockeo.vhbase.data.helper.GContestRoundType;
import com.hockeo.vhbase.data.helper.GStandingsRankType;

/**
 * Base class for international contest history records (e.g. for the CL and the WL).
 *
 * @version $Id$
 * @author jjanke
 */
@Entity
public abstract class GClubAbstractIntCupHistoryRecord extends GClubAbstractCupHistoryRecord
{
  /**
   * Number of reached ECR base points (the points are not themselves ECR points but are
   * the base for calculating them).
   */
  @Column( name = "ic_ecr_base_points" )
  protected BigDecimal d_decECRBasePoints = new BigDecimal( "0.00" );

  /**
   * Number of ECR bonus points (considered for calculating the actual ECR points).
   */
  @Column( name = "ic_ecr_bonus_points" )
  protected int        d_nECRBonusPoints  = 0;

  /**
   * Constructs a new GClubCLHistoryRecord.
   *
   * @param season the season for which to create the record
   * @param club the club to attache to the created record
   * @param round the (cup) round to which the club is initially assigned
   * @param nSeeding the seeding rank of the club for which this history record is created
   */
  public GClubAbstractIntCupHistoryRecord( GSeason season, GClub club, GContestRound round, int nSeeding )
  {
    super( season, club, round );

    setSeeding( nSeeding );
  }

  /** Default parameterless constructor. */
  protected GClubAbstractIntCupHistoryRecord()
  {}

  /**
   * Calls the super class' method {@link #reset()} and resets specific fields of this
   * class in addition.
   */
  @Override
  public void reset()
  {
    super.reset();

    d_decECRBasePoints = new BigDecimal( "0.00" );
    d_nECRBonusPoints = 0;
  }

  /**
   * Adds the results of the given KO match which has been played as part of the also
   * given contest element.
   *
   * @param element the element in which a KO match has been played
   * @param komatch the played KO match
   */
  public void addRoundResult( GKO3ContestElement element, GKOMatch komatch )
  {
    if ( !komatch.isPlayed() )
      throw new IllegalArgumentException( "The KOMatch " + komatch.getId() + " has not yet been played." );

    if ( !element.getContest().equals( getContest() ) )
      throw new IllegalArgumentException( "The contest element " + element.getId() + " is not part of the contest "
          + getContest().getId() );

    GContestRound round = element.getContestRound();

    // Is this the qualification round?
    //
    if ( round.getRoundType() == GContestRoundType.QUALIFICATION_1 )
      setNumQualificationRounds( getNumQualificationRounds() + 1 );

    if ( komatch.isWinner( getClub() ) )
    {
      setNumWonKOSeries( getNumWonKOSeries() + 1 );

      if ( round.isLastRound() )
      {
        setRank( 1 );
        setReachedRound( GContestRoundType.WINNER );
      }
    }
    else
    {
      setNumLostKOSeries( getNumLostKOSeries() + 1 );
      setRank( round.getBestPossibleLoserRank() );
    }

    setLastRoundFormat( GContestRoundFormat.KNOCK_OUT );
    setLastKOMatchName( komatch.getName() );
    setNumMatches( getNumMatches() + komatch.getNumPlayedMatches() );
    setNumWins( getNumWins() + komatch.getNumWins( getClub() ) );
    setNumWinsOT( getNumWinsOT() + komatch.getNumWinsOT( getClub() ) );
    setNumLossesOT( getNumLossesOT() + komatch.getNumLossesOT( getClub() ) );
    setNumLosses( getNumLosses() + komatch.getNumLosses( getClub() ) );
    setNumPoints( getNumPoints() + komatch.getNumPoints( getClub() ) );
    updatePointsAvgPct();
    updateECRPoints( round, komatch, null, false );
  }

  /**
   * Adds the results of a round robin contest element.
   *
   * @param element the element for which to add the result
   * @param standings the standings to be added
   */
  public void addRoundResult( GRoundRobinContestElement element, GStandings standings )
  {
    if ( !element.getContest().equals( getContest() ) )
      throw new IllegalArgumentException( "The contest element " + element.getId() + " is not part of the contest "
          + getContest().getId() );

    GContestRound round = element.getContestRound();

    // Determine rank if this is the last round or if the club is not promoted to the next
    // round
    //
    if ( round.isLastRound()
        || ( standings.getRankType() != GStandingsRankType.PROMOTED_1 && standings.getRankType() != GStandingsRankType.PROMOTED_2 ) )
    {
      // if there is more than one contest element, set the cross-element rank
      //
      if ( round.getContestElements().size() > 1 )
        setRank( standings.getCrossElementRank() );
      else
        setRank( standings.getRank() );
    }

    // if it's the last round, that the right reached round to the winner
    //
    if ( round.isLastRound() && standings.getRank() == 1 )
      setReachedRound( GContestRoundType.WINNER );

    setLastRoundFormat( GContestRoundFormat.ROUND_ROBIN );
    setLastPool( element.getLabel() );
    setLastPoolRank( standings.getRank() );
    setNumMatches( getNumMatches() + standings.getNumMatches() );
    setNumWins( getNumWins() + standings.getNumWins() );
    setNumWinsOT( getNumWinsOT() + standings.getNumWinsOT() );
    setNumLossesOT( getNumLossesOT() + standings.getNumLossesOT() );
    setNumLosses( getNumLosses() + standings.getNumLosses() );
    setNumPoints( getNumPoints() + standings.getNumPoints() );
    updatePointsAvgPct();
    updateECRPoints( round, null, standings, false );
  }

  /**
   * Removes the results of the given KO match which has been played as part of the also
   * given contest element.
   *
   * @param element the element in which a KO match has been played
   * @param komatch the played KO match
   */
  public void removeRoundResult( GKO3ContestElement element, GKOMatch komatch )
  {
    if ( !komatch.isPlayed() )
      throw new IllegalArgumentException( "The KOMatch " + komatch.getId() + " has not yet been played." );

    if ( !element.getContest().equals( getContest() ) )
      throw new IllegalArgumentException( "The contest element " + element.getId() + " is not part of the contest "
          + getContest().getId() );

    GContestRound round = element.getContestRound();

    // Is this the qualification round?
    //
    if ( round.getRoundType() == GContestRoundType.QUALIFICATION_1 )
      setNumQualificationRounds( getNumQualificationRounds() - 1 );

    if ( komatch.isWinner( getClub() ) )
      setNumWonKOSeries( getNumWonKOSeries() - 1 );
    else
      setNumLostKOSeries( getNumLostKOSeries() - 1 );

    setRank( 0 );
    setNumMatches( getNumMatches() - komatch.getNumPlayedMatches() );
    setNumWins( getNumWins() - komatch.getNumWins( getClub() ) );
    setNumWinsOT( getNumWinsOT() - komatch.getNumWinsOT( getClub() ) );
    setNumLossesOT( getNumLossesOT() - komatch.getNumLossesOT( getClub() ) );
    setNumLosses( getNumLosses() - komatch.getNumLosses( getClub() ) );
    setNumPoints( getNumPoints() - komatch.getNumPoints( getClub() ) );
    updatePointsAvgPct();
    updateECRPoints( round, komatch, null, true );
  }

  /**
   * Removes the result of the given contest element from this history record.
   *
   * @param element the element for which to remove results
   * @param standings the standings which should be removed
   */
  public void removeRoundResult( GRoundRobinContestElement element, GStandings standings )
  {
    if ( !element.getContest().equals( getContest() ) )
      throw new IllegalArgumentException( "The contest element " + element.getId() + " is not part of the contest "
          + getContest().getId() );

    GContestRound round = element.getContestRound();

    setRank( 0 );
    setNumMatches( getNumMatches() - standings.getNumMatches() );
    setNumWins( getNumWins() - standings.getNumWins() );
    setNumWinsOT( getNumWinsOT() - standings.getNumWinsOT() );
    setNumLossesOT( getNumLossesOT() - standings.getNumLossesOT() );
    setNumLosses( getNumLosses() - standings.getNumLosses() );
    setNumPoints( getNumPoints() - standings.getNumPoints() );
    updatePointsAvgPct();
    updateECRPoints( round, null, standings, true );
  }

  /**
   * (Re-)calculates the amount of ECR points currently earned in the international cup
   * for which this history object is intended for. The actual implementation must be
   * provided by the specific cup contest.
   *
   * @param roundLastAdded the last added cup contest round
   * @param komatch the last played KO match if the last round was a KO round,
   *          <code>null</code> otherwise
   * @param standings the standings if the last round was a round robin round,
   *          <code>null</code> otherwise
   * @param fRemove indicates if the points of the given KO match or standing should be
   *          removed from the current ECR points calculation
   */
  protected abstract void updateECRPoints( final GContestRound roundLastAdded, final GKOMatch komatch,
      final GStandings standings, final boolean fRemove );
}
